package com.ruoyi.dm.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.CollectFileTypeConstants;
import com.ruoyi.common.enums.CollectFileStatusEnum;
import com.ruoyi.common.exception.DataRepeatException;
import com.ruoyi.common.utils.SequenceBuilder;
import com.ruoyi.common.utils.biz.BizModule;
import com.ruoyi.common.utils.biz.PathUtil;
import com.ruoyi.dm.component.AnalysisStrategyManager;
import com.ruoyi.dm.component.DataStrategyManager;
import com.ruoyi.dm.domain.CollectEstimateVO;
import com.ruoyi.dm.domain.CollectQueryParam;
import com.ruoyi.dm.domain.EnvCollectVO;
import com.ruoyi.dm.domain.po.SpaceEnvCollectRecordPO;
import com.ruoyi.dm.mapper.SpaceEnvCollectRecordMapper;
import com.ruoyi.dm.service.ISpaceEnvCollectRecordService;
import com.ruoyi.utils.TranferUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class SpaceEnvCollectRecordServiceImpl extends ServiceImpl<SpaceEnvCollectRecordMapper, SpaceEnvCollectRecordPO> implements ISpaceEnvCollectRecordService {
    @Autowired
    private ISpaceEnvCollectRecordService recordService;


    @Autowired
    private AnalysisStrategyManager analysisStrategyManager;

    @Autowired
    private DataStrategyManager dataStrategyManager;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public IPage query(CollectQueryParam queryParam) {

        LambdaQueryWrapper<SpaceEnvCollectRecordPO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.hasLength(queryParam.getFileName()),SpaceEnvCollectRecordPO::getFileName,queryParam.getFileName());
        queryWrapper.eq(StringUtils.hasLength(queryParam.getStatus()), SpaceEnvCollectRecordPO::getStatus,queryParam.getStatus());
        queryWrapper.eq(StringUtils.hasLength(queryParam.getFileType()), SpaceEnvCollectRecordPO::getFileType,queryParam.getFileType());
        queryWrapper.eq(StringUtils.hasLength(queryParam.getDataType()), SpaceEnvCollectRecordPO::getDataType,queryParam.getDataType());
        queryWrapper.orderByDesc(SpaceEnvCollectRecordPO::getFileDate);
        queryWrapper.between(null != queryParam.getStartTime() && null!= queryParam.getEndTime(),SpaceEnvCollectRecordPO::getFileDate,queryParam.getStartTime(),queryParam.getEndTime());
        IPage page = recordService.page(queryParam.page(), queryWrapper);
        List<EnvCollectVO> objects = new ArrayList<>();
        for (Object airCollectRecordPO : page.getRecords()) {
            SpaceEnvCollectRecordPO po = (SpaceEnvCollectRecordPO) airCollectRecordPO;
            EnvCollectVO vo = TranferUtils.tranferEnvRecordPO(po);
            objects.add(vo);
        }
        page.setRecords(objects);
        return page;
    }

    @Override
    public List<String> uploadFile(MultipartFile[] files, String fileType, String dataType) throws DataRepeatException {

        //单个文件,如果重复,直接抛异常
        if (files.length==1) {
            String filename = files[0].getOriginalFilename();
            int num = this.checkRepeat(filename);
            if (num>0) {
                //存在 不处理
                throw new DataRepeatException();
            }
        }

        List<SpaceEnvCollectRecordPO> list = new ArrayList<>(files.length);
        Date now = new Date();
        String midPath = "/env/"+fileType;
        String fullPath = PathUtil.outputModuleDir(BizModule.COLLECT)+midPath;
        List<String> resList = new ArrayList<>();
        for (MultipartFile file : files) {
            String filename = file.getOriginalFilename();
            int num = this.checkRepeat(filename);
            if (num>0) {
                //存在 不处理
                resList.add(filename);
                continue;
            }

            SpaceEnvCollectRecordPO recordPO = new SpaceEnvCollectRecordPO();
            recordPO.setFileName(filename);
            recordPO.setFileType(fileType);
            recordPO.setFileSize(file.getSize());
            recordPO.setUploadTime(now);
            recordPO.setFilePath(midPath);
            recordPO.setDataType(dataType);
            recordPO.setStatus(CollectFileStatusEnum.FILE_WAIT.getCode());
            try {
                File path = new File(fullPath, filename);
                FileUtil.mkParentDirs(path);
                file.transferTo(path);
                //通过filename 获取 信息
                //选择数据策略,并进行相应的的特殊化处理
                dataStrategyManager.switchStrategy(fileType,null,null).makeupPo(recordPO);
            }catch (Exception e) {
                log.error("",e);
                recordPO.setStatus(CollectFileStatusEnum.FILE_ERROR.getCode());
                recordPO.setFileDesc("保存异常，"+e.getMessage());
            }

            list.add(recordPO);
        }
        //保存到数据库
        recordService.saveBatch(list);
        for (SpaceEnvCollectRecordPO po : list) {
            analysisStrategyManager.analysisEnv(po, null);
        }
        return resList;
    }

    @Override
    public void uploadFileByFile(File[] files, String fileType, String dataType) throws DataRepeatException {

        //单个文件,如果重复,直接抛异常
        if (files.length==1) {
            String filename = files[0].getName();
            int num = this.checkRepeat(filename);
            if (num>0) {
                //存在 不处理
                throw new DataRepeatException();
            }
        }

        List<SpaceEnvCollectRecordPO> list = new ArrayList<>(files.length);
        Date now = new Date();
        String midPath = "/env/"+fileType;
        String fullPath = PathUtil.outputModuleDir(BizModule.COLLECT)+midPath;
        for (File file : files) {
            String filename = file.getName();
            int num = this.checkRepeat(filename);
            if (num>0) {
                //存在 不处理
                log.info(filename+" 重复,不处理");
                continue;
            }

            SpaceEnvCollectRecordPO recordPO = new SpaceEnvCollectRecordPO();
            recordPO.setFileName(filename);
            recordPO.setFileType(fileType);
            recordPO.setFileSize(file.length());
            recordPO.setUploadTime(now);
            recordPO.setFilePath(midPath);
            recordPO.setDataType(dataType);
            recordPO.setStatus(CollectFileStatusEnum.FILE_WAIT.getCode());
            try {
                File path = new File(fullPath, filename);
                FileUtil.mkParentDirs(path);
                FileUtil.copy(file,path,false);
                //通过filename 获取 信息
                //选择数据策略,并进行相应的的特殊化处理
                dataStrategyManager.switchStrategy(fileType,null,null).makeupPo(recordPO);
            }catch (Exception e) {
                log.error("",e);
                recordPO.setStatus(CollectFileStatusEnum.FILE_ERROR.getCode());
                recordPO.setFileDesc("保存异常，"+e.getMessage());
            }

            list.add(recordPO);
        }
        //保存到数据库
        recordService.saveBatch(list);
        for (SpaceEnvCollectRecordPO po : list) {
            analysisStrategyManager.analysisEnv(po, null);
        }
    }


    @Override
    public boolean delFile(Long id) {
        LambdaQueryWrapper<SpaceEnvCollectRecordPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(SpaceEnvCollectRecordPO::getFilePath,SpaceEnvCollectRecordPO::getFileName);
        wrapper.eq(SpaceEnvCollectRecordPO::getId,id);
        SpaceEnvCollectRecordPO po = recordService.getOne(wrapper);
        String fullPath = PathUtil.outputModuleDir(BizModule.COLLECT)+po.getFilePath()+"/"+ po.getFileName();
        new File(fullPath).delete();
        return recordService.removeById(id);
    }

    @Override
    public boolean delBatch(Long[] delParam) {
        LambdaQueryWrapper<SpaceEnvCollectRecordPO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(SpaceEnvCollectRecordPO::getId,delParam);
        queryWrapper.select(SpaceEnvCollectRecordPO::getFilePath,SpaceEnvCollectRecordPO::getFileName);
        List<SpaceEnvCollectRecordPO> pos = recordService.list(queryWrapper);
        if (null != pos) {
            for (SpaceEnvCollectRecordPO po : pos) {
                String fullPath = PathUtil.outputModuleDir(BizModule.COLLECT)+po.getFilePath()+"/"+ po.getFileName();
                new File(fullPath).delete();
            }
        }

        return recordService.remove(queryWrapper);
    }

    @Override
    public String download(Long id) {
        LambdaQueryWrapper<SpaceEnvCollectRecordPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(SpaceEnvCollectRecordPO::getFilePath,SpaceEnvCollectRecordPO::getFileName);
        wrapper.eq(SpaceEnvCollectRecordPO::getId,id);
        SpaceEnvCollectRecordPO po = recordService.getOne(wrapper);
        return PathUtil.outputModuleDir(BizModule.COLLECT)+po.getFilePath()+"/"+ po.getFileName();

    }

    @Override
    public String downloadBatch(Long[] ids) {
        LambdaQueryWrapper<SpaceEnvCollectRecordPO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(SpaceEnvCollectRecordPO::getId,ids);
        List<SpaceEnvCollectRecordPO> list = recordService.list(queryWrapper);
        String path = PathUtil.tmpModuleDir(BizModule.COLLECT)+"/"+ DateUtil.format(new Date(),"yyyyMMddHHmmss")+".zip";
        if (CollectionUtil.isNotEmpty(list)) {
            File[] toZipArr = new File[list.size()];
            for (int i = 0; i < list.size(); i++) {
                String fullPath = PathUtil.outputModuleDir(BizModule.COLLECT)+list.get(i).getFilePath()+"/"+list.get(i).getFileName();
                toZipArr[i] = new File(fullPath);
            }
            ZipUtil.zip(new File(path), false, toZipArr);
        }
        return path;
    }


    @Override
    public boolean analysis(Long id, String method) {
        LambdaQueryWrapper<SpaceEnvCollectRecordPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(SpaceEnvCollectRecordPO::getId,SpaceEnvCollectRecordPO::getFileDate,SpaceEnvCollectRecordPO::getFilePath,SpaceEnvCollectRecordPO::getFileName,SpaceEnvCollectRecordPO::getFileType,SpaceEnvCollectRecordPO::getDataType);
        wrapper.eq(SpaceEnvCollectRecordPO::getId,id);
        SpaceEnvCollectRecordPO po = recordService.getOne(wrapper);

        LambdaUpdateWrapper<SpaceEnvCollectRecordPO> uw = new LambdaUpdateWrapper<>();
        uw.eq(SpaceEnvCollectRecordPO::getId,id);
        uw.set(SpaceEnvCollectRecordPO::getStatus,CollectFileStatusEnum.FILE_WAIT.getCode());
        recordService.update(uw);

        analysisStrategyManager.analysisEnv(po,method);
        return true;
    }

    @Override
    public boolean analysisBatch(Long[] ids, String method) {
        LambdaQueryWrapper<SpaceEnvCollectRecordPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(SpaceEnvCollectRecordPO::getId,SpaceEnvCollectRecordPO::getFileDate,SpaceEnvCollectRecordPO::getFilePath,SpaceEnvCollectRecordPO::getFileName,SpaceEnvCollectRecordPO::getFileType,SpaceEnvCollectRecordPO::getDataType);
        wrapper.in(SpaceEnvCollectRecordPO::getId,ids);
        List<SpaceEnvCollectRecordPO> list = recordService.list(wrapper);
        LambdaUpdateWrapper<SpaceEnvCollectRecordPO> uw = new LambdaUpdateWrapper<>();
        uw.in(SpaceEnvCollectRecordPO::getId,ids);
        uw.set(SpaceEnvCollectRecordPO::getStatus,CollectFileStatusEnum.FILE_WAIT.getCode());
        recordService.update(uw);
        if (CollectionUtil.isNotEmpty(list)) {
            for (SpaceEnvCollectRecordPO po : list) {
                analysisStrategyManager.analysisEnv(po, method);
            }
        }
        return true;
    }

    @Override
    public CollectEstimateVO lookRes(Long id) throws Exception {
        LambdaQueryWrapper<SpaceEnvCollectRecordPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(SpaceEnvCollectRecordPO::getResFile);
        wrapper.eq(SpaceEnvCollectRecordPO::getId,id);
        SpaceEnvCollectRecordPO po = recordService.getOne(wrapper);
        if (null != po && StringUtils.hasLength(po.getResFile())) {
            JSONObject jsonObject = JSON.parseObject(po.getResFile());
            CollectEstimateVO collectEstimateVO = new CollectEstimateVO();
            collectEstimateVO.setToal(jsonObject.getInteger("total"));
            collectEstimateVO.setError(jsonObject.getInteger("error"));
            return collectEstimateVO;
        }else {
            throw  new Exception("数据不存在或解析异常");
        }
    }

    @Override
    public String lookDetailRes(Long id) throws Exception {
        LambdaQueryWrapper<SpaceEnvCollectRecordPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(SpaceEnvCollectRecordPO::getResFile);
        wrapper.eq(SpaceEnvCollectRecordPO::getId,id);
        SpaceEnvCollectRecordPO po = recordService.getOne(wrapper);
        if (null != po && StringUtils.hasLength(po.getResFile())) {
            JSONObject jsonObject = JSON.parseObject(po.getResFile());
            String logFile = jsonObject.getString("logFile");
            String content = FileUtil.readString(logFile,Charset.forName("UTF-8"));
            return content;
        }else {
            throw  new Exception("数据不存在或解析异常");
        }
    }

    @Override
    public int checkRepeat(String filename) {
        return this.getBaseMapper().selectRepeat(filename);
    }

    @Override
    public void forcast(String fileType, String dataType, Date fileDate) {
        //根据不同算法,得到结果 分别存入表
        // 太阳 调用3种算法  地磁 只调用 LSTM
        //预报结果  覆盖更新,只保存最新数据
        List<String> commonds = new ArrayList<>();
        String script = PathUtil.codeModuleDir(BizModule.MODEL_CAL) + PathUtil.moduleDirs().getModelCal().get("forecast");
        DateTime futureDay = null;
        // 预报  需 太阳黑子 和 S107 M107 Y107 共同存在数据. 故先判断预报时间是否 合适
        //查看太阳黑子的最新数据 时间
        Date snDate = jdbcTemplate.queryForObject("select max(\"DATETIME\") FROM SWDATA_OBS_SN" , Date.class);
        //取  预报时间  和 太阳黑子 时间的 小值
        if (fileDate.after(snDate)) {
            log.info("开始预报任务..,取太阳黑子时间");
            futureDay = DateUtil.offset(snDate, DateField.DAY_OF_MONTH, 1);
        }else {
            log.info("开始预报任务..,取指数时间");
            futureDay = DateUtil.offset(fileDate, DateField.DAY_OF_MONTH, 1);
        }
        String dateStr = DateUtil.format(futureDay, "yyyy-MM-dd-");
        String futureDayStr = DateUtil.format(futureDay, "yyyy-MM-dd HH:mm:ss");
        // AP -> Ap  KP -> Kp  DST -> Dst
        if ("AP".equals(dataType)) {
            dataType="Ap";
        }else if ("KP".equals(dataType)) {
            dataType="Kp";
        }else if ("DST".equals(dataType)) {
            dataType="Dst";
        }
        log.info("开始预报任务..{},{},{}",fileType,dataType,dateStr);
        commonds.add("python");
        commonds.add(script);
        commonds.add(dataType);
        commonds.add(dateStr);
        commonds.add("");
        List<String> dateList = null;
        List<String> ar54List = null;
        List<String> lstmList = null;
        //List<String> multList = null;
        List<String> svmList = null;

        if (CollectFileTypeConstants.TYPE_SUN.equals(fileType)) {
            String tableName = "SWDATA_FUTURE_SOLAR_"+dataType.toUpperCase();
            commonds.set(4, "54AR");
             {
                String res = RuntimeUtil.execForStr(CollectionUtil.join(commonds, " "));
                JSONObject parseObject = JSON.parseObject(res);
                dateList = parseObject.getList("DATETIME", String.class);
                log.info("开始执行太阳辐射指数预报--{}--{}","54AR",dateList.size());
                ar54List = parseObject.getList("DATA", String.class);
            }
            //commonds.set(4, "LSTM");
            // {
            //    String res = RuntimeUtil.execForStr(CollectionUtil.join(commonds, " "));
            //    JSONObject parseObject = JSON.parseObject(res);
            //    lstmList = parseObject.getList("DATA", String.class);
            //    log.info("开始执行太阳辐射指数预报--{}--{}","LSTM",lstmList.size());
            //}
            //commonds.set(4, "MULISPLINE");
            //{
            //    String res = RuntimeUtil.execForStr(CollectionUtil.join(commonds, " "));
            //    JSONObject parseObject = JSON.parseObject(res);
            //    multList = parseObject.getList("DATA", String.class);
            //    log.info("开始执行太阳辐射指数预报--{}--{}","MULISPLINE",multList.size());
            //}
            commonds.set(4, "SVM");
            {
                String res = RuntimeUtil.execForStr(CollectionUtil.join(commonds, " "));
                JSONObject parseObject = JSON.parseObject(res);
                svmList = parseObject.getList("DATA", String.class);
                log.info("开始执行太阳辐射指数预报--{}--{}","svm",svmList.size());
            }
            log.info("开始执行太阳辐射指数预报入库....{}",dateList.size());
            //54Ar
            for (int i = 0; i < dateList.size(); i++) {
                long id = SequenceBuilder.sequenceId();
                String sql = "INSERT INTO %s (ID, PRE_TIME, DATETIME, METHOD, VALUE) VALUES(%s, TO_DATE('%s','yyyy-MM-dd hh24:mi:ss'), TO_DATE('%s','yyyy-MM-dd hh24:mi:ss'), '%s', %s)";
                String sunInsertSql = String.format(sql,tableName,id,futureDayStr,dateList.get(i),"54AR",ar54List.get(i));
                jdbcTemplate.update(sunInsertSql);
            }

            //svm
            for (int i = 0; i < dateList.size(); i++) {
                long id = SequenceBuilder.sequenceId();
                String sql = "INSERT INTO %s (ID, PRE_TIME, DATETIME, METHOD, VALUE) VALUES(%s, TO_DATE('%s','yyyy-MM-dd hh24:mi:ss'), TO_DATE('%s','yyyy-MM-dd hh24:mi:ss'), '%s', %s)";
                String sunInsertSql = String.format(sql,tableName,id,futureDayStr,dateList.get(i),"SVM",svmList.get(i));
                jdbcTemplate.update(sunInsertSql);
            }

        } else if (CollectFileTypeConstants.TYPE_EARTH.equals(fileType)) {

            String tableName = "SWDATA_FUTURE_MAG_"+dataType.toUpperCase();
            commonds.set(4, "LSTM");
             {
                String res = RuntimeUtil.execForStr(CollectionUtil.join(commonds, " "));
                JSONObject parseObject = JSON.parseObject(res);
                dateList = parseObject.getList("DATETIME", String.class);
                log.info("开始执行地磁指数预报--{}--{}","LSTM",dateList.size());
                lstmList = parseObject.getList("DATA", String.class);
            }
            log.info("开始执行地磁指数预报入库....{}",dateList.size());
            for (int i = 0; i < dateList.size(); i++) {
                long id = SequenceBuilder.sequenceId();
                String sql = "INSERT INTO %s (ID, PRE_TIME, DATETIME, METHOD, VALUE) VALUES(%s, TO_DATE('%s','yyyy-MM-dd hh24:mi:ss'), TO_DATE('%s','yyyy-MM-dd hh24:mi:ss'), '%s', %s)";
                String sunInsertSql = String.format(sql,tableName,id,futureDayStr,dateList.get(i),"LSTM",lstmList.get(i));
                jdbcTemplate.update(sunInsertSql);
            }
        }
        log.info("预报任务结束..");
    }



    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized void afterAnalysis(SpaceEnvCollectRecordPO po, String outFile) throws Exception {

        dataStrategyManager.switchStrategy(po.getFileType(), null, null).save(po.getDataType(),outFile);
        if ("SN".equals(po.getDataType())) {
            //太阳黑子不触发预报
        }else {
            //触发预报
            //似乎不需要自动触发,页面手动触发
            //this.forcast(po.getFileType(), po.getDataType(), po.getFileDate());
        }
    }
}
